﻿using System;
using System.Collections.Generic;
using System.Text;

namespace HZY.Toolkit
{
    using System.Data;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Reflection;

    public static class LinqUtil
    {



        /// <summary>
        /// 将 Model 转换为 MemberInitExpression 类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="Model"></param>
        /// <returns></returns>
        public static MemberInitExpression ModelToMemberInitExpression<T>(T Model)
        {
            var proInfo = GetPropertyInfos(typeof(T));

            var list = new List<MemberBinding>();

            foreach (var item in proInfo) list.Add(Expression.Bind(item, Expression.Constant(item.GetValue(Model), item.PropertyType)));

            var newExpr = Expression.New(typeof(T));

            return Expression.MemberInit(newExpr, list);
        }

        /// <summary>
        /// 获取 PropertyInfo 集合
        /// </summary>
        /// <param name="_type"></param>
        /// <param name="_bindingFlags"></param>
        /// <returns></returns>
        public static PropertyInfo[] GetPropertyInfos(Type _type, BindingFlags _bindingFlags = (BindingFlags.Instance | BindingFlags.Public)) => _type.GetProperties(_bindingFlags);

        /// <summary>
        /// 创建 对象实例
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static T CreateInstance<T>()
        {
            var _Type = typeof(T);
            if (_Type.IsValueType || typeof(T) == typeof(string))
                return default(T);
            return (T)Activator.CreateInstance(_Type);
        }

        /// <summary>
        /// 获取 对象 中 某个属性得 标记
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="_type"></param>
        /// <param name="_name"></param>
        /// <returns></returns>
        public static T GetAttribute<T>(Type _type, string _name)
            where T : Attribute
            => GetPropertyInfo(_type, _name).GetCustomAttribute(typeof(T)) as T;

        /// <summary>
        /// 获取 PropertyInfo 对象
        /// </summary>
        /// <param name="_type"></param>
        /// <param name="_name"></param>
        /// <returns></returns>
        public static PropertyInfo GetPropertyInfo(Type _type, string _name) => _type.GetProperty(_name);



        #region 查询 辅助函数

        /// <summary>
        /// Eval
        /// </summary>
        /// <param name="_Expression"></param>
        /// <returns></returns>
        public static object Eval(Expression _Expression)
        {
            var cast = Expression.Convert(_Expression, typeof(object));
            return Expression.Lambda<Func<object>>(cast).Compile().Invoke();
        }

        /// <summary>
        /// 根据实体对象 的 ID 创建 Expression<Func<HzyTuple<T>, bool>> 表达式树 例如： Lambda = ( w=>w.t1.Key==Guid.Empty )
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="_KeyName"></param>
        /// <param name="_KeyValue"></param>
        /// <returns></returns>
        public static Expression<Func<T, bool>> WhereById<T>(string _KeyName, object _KeyValue, string _ParName = "_Where_Parameter")
        {
            //创建 Where Lambda表达式树
            var _Type = typeof(T);
            var _Parmeter = Expression.Parameter(_Type, "w");
            var _Where_Parameter = Expression.Parameter(_Type, _ParName);
            var _Property = _Type.GetProperty(nameof(T));
            //元组参数
            var _Left = Expression.Property(_Parmeter, _Property);
            //字段名
            var _KeyProperty = _Property.PropertyType.GetProperty(_KeyName);
            //w=>w.t1.Key
            var _NewLeft = Expression.Property(_Left, _KeyProperty);
            //==Guid.Empty
            var _Sign = _KeyValue == null;
            if (!_Sign) _Sign = (_KeyValue is string & string.IsNullOrWhiteSpace(_KeyValue.ToString()));
            if (_KeyProperty.PropertyType == typeof(Guid) & _Sign) _KeyValue = Guid.Empty;
            if (_KeyProperty.PropertyType == typeof(int) & _Sign) _KeyValue = Int32.MinValue;
            if (_KeyProperty.PropertyType == typeof(Guid) & !_Sign) _KeyValue = Guid.Parse(_KeyValue.ToString());
            if (_KeyProperty.PropertyType == typeof(int) & !_Sign) _KeyValue = Int32.Parse(_KeyValue.ToString());
            try
            {
                var _Where_Body = Expression.Equal(_NewLeft, Expression.Constant(_KeyValue, _KeyProperty.PropertyType));
                return Expression.Lambda<Func<T, bool>>(_Where_Body, _Where_Parameter);
            }
            catch (Exception ex)
            {
                if (_KeyProperty.PropertyType != _KeyValue.GetType())
                    throw new Exception("请将主键值 转换为 正确的类型值！", ex);
                else
                    throw;
            }
        }

        #endregion

        /// <summary>
        /// DataRow 转换 实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dr"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this DataRow dr) where T : class, new()
        {
            var _Entity = CreateInstance<T>();
            var list = _Entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

            if (list.Length == 0) throw new Exception("找不到任何 公共属性！");

            foreach (var item in list)
            {
                string AttrName = item.Name;
                if (!dr.Table.Columns.Contains(AttrName)) continue;
                if (dr[AttrName] == DBNull.Value) continue;
                item.SetValue(_Entity, dr[AttrName], null);
            }
            return _Entity;
        }

        /// <summary>
        /// IDataReader 转换 实体
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dr"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this IDataReader _IDataReader) where T : class, new()
        {
            var _Entity = CreateInstance<T>();
            var list = _Entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

            if (list.Length == 0) throw new Exception("找不到任何 公共属性！");

            List<string> field = new List<string>(_IDataReader.FieldCount);
            for (int i = 0; i < _IDataReader.FieldCount; i++)
            {
                field.Add(_IDataReader.GetName(i));
            }

            foreach (var item in list)
            {
                string AttrName = item.Name;
                if (!field.Contains(AttrName)) continue;
                if (_IDataReader[AttrName] == DBNull.Value) continue;
                item.SetValue(_Entity, _IDataReader[AttrName], null);
            }
            return _Entity;
        }

        /// <summary>
        /// 将 datatable 转换为 list
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="table"></param>
        /// <returns></returns>
        public static List<T> ToList<T>(this DataTable table) where T : class, new()
        {
            var list = new List<T>();

            var _Entity = CreateInstance<T>();
            var propertyInfo = _Entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (DataRow dr in table.Rows)
            {
                _Entity = CreateInstance<T>();
                foreach (var item in propertyInfo)
                {
                    string AttrName = item.Name;
                    if (!dr.Table.Columns.Contains(AttrName)) continue;
                    if (dr[AttrName] == DBNull.Value) continue;
                    item.SetValue(_Entity, dr[AttrName], null);
                }
                list.Add(_Entity);
            }
            return list;
        }

        /// <summary>
        /// datatable 转换为 List<Dictionary<string,object>>
        /// </summary>
        /// <param name="table"></param>
        /// <returns></returns>
        public static List<Dictionary<string, object>> ToList(this DataTable table, string DateTimeStringFormat = "yyyy-MM-dd HH:mm", Action<Type, object> Success = null)
        {
            var list = new List<Dictionary<string, object>>();
            var dic = new Dictionary<string, object>();
            foreach (DataRow dr in table.Rows)
            {
                if (dic != null) dic = new Dictionary<string, object>();
                foreach (DataColumn dc in table.Columns)
                {
                    object value = null;
                    if (dr[dc.ColumnName] != DBNull.Value)
                        value = (dc.DataType == typeof(DateTime)) ? Convert.ToDateTime(dr[dc.ColumnName]).ToString(DateTimeStringFormat) : dr[dc.ColumnName];
                    Success?.Invoke(dc.DataType, value);
                    dic.Add(dc.ColumnName, value);
                }
                list.Add(dic);
            }
            return list;
        }

        #region IDataReader TO List<Dictionary<string, object>> And DataTable

        /// <summary>
        /// 将 IDataReader 对象转换为 List<T>
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="_IDataReader"></param>
        /// <returns></returns>
        public static List<T> ToList<T>(this IDataReader _IDataReader)
        {
            if (_IDataReader.IsClosed) throw new Exception("IDataReader 对象连接已关闭！");

            var res = new List<T>();

            var _Entity = CreateInstance<T>();
            var propertyInfo = _Entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

            List<string> field = new List<string>(_IDataReader.FieldCount);
            for (int i = 0; i < _IDataReader.FieldCount; i++)
            {
                field.Add(_IDataReader.GetName(i));
            }

            while (_IDataReader.Read())
            {
                _Entity = CreateInstance<T>();
                foreach (var item in propertyInfo)
                {
                    string AttrName = item.Name;
                    if (!field.Contains(AttrName)) continue;
                    if (_IDataReader[AttrName] == DBNull.Value) continue;
                    item.SetValue(_Entity, _IDataReader[AttrName], null);
                }
                res.Add(_Entity);
            }

            return res;
        }

        /// <summary>
        /// 将IDataReader对象转换为字典
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="_IDataReader"></param>
        /// <returns></returns>
        public static List<Dictionary<string, object>> ToList(this IDataReader _IDataReader)
        {
            if (_IDataReader.IsClosed) throw new Exception("IDataReader 对象连接已关闭！");

            var res = new List<Dictionary<string, object>>();

            List<string> field = new List<string>(_IDataReader.FieldCount);
            for (int i = 0; i < _IDataReader.FieldCount; i++)
            {
                field.Add(_IDataReader.GetName(i));
            }

            while (_IDataReader.Read())
            {
                var dic = new Dictionary<string, object>();
                foreach (var item in field)
                {
                    dic[item] = (_IDataReader[item] == DBNull.Value) ? null : _IDataReader[item];
                }

                res.Add(dic);
            }

            return res;
        }

        /// <summary>
        /// IDataReader 转换为 DataTable
        /// </summary>
        /// <param name="_IDataReader"></param>
        /// <returns></returns>
        public static DataTable ToDataTable(this IDataReader _IDataReader)
        {
            DataTable dt = new DataTable();
            dt.Load(_IDataReader);
            return dt;
        }

        #endregion



        /// <summary>
        /// 将匿名对象转换为字典
        /// </summary>
        /// <param name="Attribute"></param>
        /// <returns></returns>
        public static Dictionary<string, object> ToDic<T>(this T Attribute) where T : class, new()
        {
            var di = new Dictionary<string, object>();

            Type _type = Attribute.GetType();

            var fields = _type.GetProperties().ToList();

            foreach (var item in fields) di.Add(item.Name, item.GetValue(Attribute).ToString());

            return di;
        }

        /// <summary>
        /// 将 T 实体中数据 转到 M 实体中去
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="M"></typeparam>
        /// <param name="Entity"></param>
        /// <param name="Model"></param>
        /// <returns></returns>
        public static M ToNewEntity<T, M>(this T Entity, M Model) where T : class, new()
        {
            //old
            var oldList = Entity.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);
            var newList = Model.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public);

            foreach (var oldM in oldList)
            {
                foreach (var newM in newList)
                {
                    if (oldM.Name != newM.Name) continue;
                    if (oldM.PropertyType == typeof(bool) & (newM.PropertyType == typeof(int) | newM.PropertyType == typeof(int?)))
                    {
                        var _Value = (bool)oldM.GetValue(Entity);
                        newM.SetValue(Model, _Value ? 1 : 0);
                    }
                    else if (newM.PropertyType == typeof(bool) & (oldM.PropertyType == typeof(int) | oldM.PropertyType == typeof(int?)))
                    {
                        var _Value = (int?)oldM.GetValue(Entity);
                        newM.SetValue(Model, _Value == null ? false : (_Value == 1 ? true : false));
                    }
                    else
                    {
                        newM.SetValue(Model, oldM.GetValue(Entity));
                    }
                }
            }

            return Model;
        }

        /// <summary>
        /// 将 T 集合数据 copy 到 M 集合中
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="M"></typeparam>
        /// <param name="EntityList"></param>
        /// <param name="ModelList"></param>
        /// <returns></returns>
        public static List<M> ToNewList<T, M>(this List<T> EntityList, List<M> ModelList)
            where M : class, new()
            where T : class, new()
        {
            foreach (var item in EntityList)
            {
                var _M = new M();
                item.ToNewEntity(_M);
                ModelList.Add(_M);
            }
            return ModelList;
        }










    }
}
